package com.it.product.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.it.product.domain.*;
import com.it.product.mapper.*;
import com.it.product.service.IProductService;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.common.core.web.domain.BaseEntity;
import com.spzx.common.redis.cache.GuiguCache;
import com.spzx.common.redis.configure.RedissonConfig;
import com.spzx.product.api.domain.*;

import com.spzx.product.api.domain.vo.SkuPrice;
import com.spzx.product.api.domain.vo.SkuQuery;
import com.spzx.product.api.domain.vo.SkuStockVo;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * projectName: spzx-parent
 *
 * @author: 王宝贵
 * time: 2024/9/22 21:28 周日
 * description:
 */
@Service
@Slf4j
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements IProductService {

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private CategoryBrandMapper categoryBrandMapper;

    @Autowired
    private ProductSpecMapper productSpecMapper;

    // 新增商品操作需要的mapper
    @Autowired
    private ProductDetailsMapper productDetailsMapper;
    @Autowired
    private ProductSkuMapper productSkuMapper;
    @Autowired
    private SkuStockMapper skuStockMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 分页查询商品管理
     *
     * @param product
     * @return
     */
    @Override
    public List<Product> selectPage(Product product) {
        return productMapper.selectListPage(product);
    }

    /**
     * 加载商品数据
     *
     * @param id 分类的id
     * @return 品牌的名字
     */
    @Override
    public List<Brand> GetBrandNameById(Long id) {
        return categoryBrandMapper.GetBrandNameById(id);
    }

    /**
     * 3.
     * 根据分类id查询商品规格的名字
     *
     * @param categoryId
     * @return
     */
    @Override
    public List<ProductSpec> selectSpecNameById(Long categoryId) {

        List<ProductSpec> productSpecList = productSpecMapper.selectList(new LambdaQueryWrapper<ProductSpec>().eq(ProductSpec::getCategoryId, categoryId));

        return productSpecList;
    }

    /**
     * 新增商品
     *
     * @param product 四张表的数据
     * @return
     */
    @Override
    public int saveList(Product product) {
        //1.先新增商品的基本数据
        productMapper.insert(product);

        //2.新增商品sku列表
        List<ProductSku> productSkuList = product.getProductSkuList();
        //获取单独的sku信息
        for (int i = 0; i < productSkuList.size(); i++) {
            ProductSku productSku = productSkuList.get(i);
            //商品的Skucode属性
            productSku.setSkuCode(product.getId() + "_" + i);
            //商品的SkuProduct属性id
            productSku.setProductId(product.getId());
            //商品的sku属性--名字
            String SkuName = product.getName() + " " + productSku.getSkuSpec();
            productSku.setSkuName(SkuName);
            productSkuMapper.insert(productSku);

            //3.新增商品库存表
            SkuStock skuStock = new SkuStock();
            skuStock.setSkuId(productSku.getId());
            //总库存和可用库存都是前端传过来的总库存
            skuStock.setTotalNum(productSku.getStockNum());
            skuStock.setAvailableNum(productSku.getStockNum());
            //销量和锁定库存都为0
            skuStock.setLockNum(0);
            skuStock.setSaleNum(0);
            skuStockMapper.insert(skuStock);
        }


        //4.新增图片的详情表
        ProductDetails productDetails = new ProductDetails();
        List<String> detailsImageUrlList = product.getDetailsImageUrlList();
        //商品详情表的id
        productDetails.setProductId(product.getId());
        //商品详情图片们的地址 ---join 将特点的字符串集合拆分组成一个新的字符串
        String urls = String.join(",", product.getDetailsImageUrlList());
        productDetails.setImageUrls(urls);
        productDetailsMapper.insert(productDetails);

        // 将包装转为基本 ---valueOf反过来的
        Long id = 55L;
        int i = id.intValue();
        return 1;
    }

    /**
     * 根据商品的id 查询商品的信息
     *
     * @param id 商品的id
     * @return
     */
    @Override
    public Product selectByproductId(Long id) {
        //1.先查询基本的商品信息
        Product product = productMapper.selectById(id);
        //2.查询商品sku列表
        List<ProductSku> productSkus = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>().eq(ProductSku::getProductId, id));

        //3.查询库存信息
        productSkus.forEach(productSku -> {
            Long skuId = productSku.getId();
            List<SkuStock> skuStocks = skuStockMapper.selectList(new LambdaQueryWrapper<SkuStock>().eq(SkuStock::getSkuId, skuId));
            skuStocks.forEach(skuStock -> {
                productSku.setStockNum(skuStock.getTotalNum());
            });
        });
        //4.查询图片信息
        ProductDetails productDetails = productDetailsMapper.selectOne(new LambdaQueryWrapper<ProductDetails>().eq(ProductDetails::getProductId, id));
        product.setProductSkuList(productSkus);
        //将图片信息拆开
        String[] url = productDetails.getImageUrls().split(",");
        product.setDetailsImageUrlList(Arrays.asList(url));

        return product;
    }


    /**
     * 修改商品
     *
     * @param product
     * @return
     */
    @Override
    public int updateProduct(Product product) {
        //1.先修改商品表的信息
        productMapper.updateById(product);

        //2.修改商品sku表
        List<ProductSku> productSkuList = product.getProductSkuList();
        productSkuList.forEach(productSku -> {
            productSkuMapper.updateById(productSku);

            //3.修改库存表
            Integer stockNum = productSku.getStockNum();
            LambdaQueryWrapper<SkuStock> lambdaQueryWrapper = new LambdaQueryWrapper<SkuStock>().eq(SkuStock::getSkuId, productSku.getId());
            // TODO 修改库存的可用库存 用数据库的总库存-锁定库存
            SkuStock skuStock = skuStockMapper.selectOne(lambdaQueryWrapper);
            //总库存
            skuStock.setTotalNum(stockNum);
            //可用库存
            skuStock.setAvailableNum(stockNum - skuStock.getLockNum());
            skuStockMapper.update(skuStock, lambdaQueryWrapper);
        });
        //4.修改sku属性表
        List<String> detailsImageUrlList = product.getDetailsImageUrlList();
        //记那个集合按照指定字符拼接为一个字符串
        String urls = String.join(",", detailsImageUrlList);
        //从数据库中查询出一个SkuStock对象
        /*  ProductDetails productDetails = productDetailsMapper.selectOne(new LambdaQueryWrapper<ProductDetails>().eq(ProductDetails::getProductId, product.getId()));
         */
        ProductDetails productDetails = new ProductDetails();
        productDetails.setProductId(product.getId());
        productDetails.setImageUrls(urls);

        //再修改
        productDetailsMapper.updateById(productDetails);
        return 1;
    }

    /**
     * 根据商品id删除商品
     *
     * @param ids 商品id
     * @return
     */
    @Override
    public int removeProductByIds(List<Long> ids) {
        //1.先删除商品的基本信息
        productMapper.deleteBatchIds(ids);


        //3.删除商品的库存表信息--需要使用sku的id
        List<ProductSku> productSkus = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>().in(ProductSku::getProductId, ids));
        productSkus.forEach(productSku -> {
            skuStockMapper.delete(new LambdaQueryWrapper<SkuStock>().eq(SkuStock::getSkuId, productSku.getId()));
        });

        //2.删除商品sku信息--如果先删除sku 就查不到id 删不了库存表
        productSkuMapper.delete(new LambdaQueryWrapper<ProductSku>().in(ProductSku::getProductId, ids));

        //4.删除库存信息
        productDetailsMapper.delete(new LambdaQueryWrapper<ProductDetails>().in(ProductDetails::getProductId, ids));

        return 1;
    }

    /**
     * 根据商品的id修改商品的状态
     *
     * @param id          商品的id
     * @param auditStatus 商品的状态
     * @return
     */
    @Override
    public int updateAuditStatus(Long id, Integer auditStatus) {
        Product product = productMapper.selectById(id);
        if (auditStatus > 0) {
            //通过
            product.setAuditStatus(1);
            product.setAuditMessage("审核通过");
            //加入到bloomFilter
            RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter("sku:bloom:filter");
            List<ProductSku> productSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>().eq(ProductSku::getProductId, id));
           productSkuList.forEach(item->{
               bloomFilter.add(item.getId());
           });
        } else {
            //不通过
            product.setAuditStatus(-1);
            product.setAuditMessage("审核不通过");
        }
        productMapper.updateById(product);
        return 1;
    }


    /**
     * 更新商品的上下架状态
     *
     * @param id     商品的id
     * @param status 商品的状态 1 上架 -1下架
     * @return
     */
    @Override
    public int updateStatus(Long id, Integer status) {
        Product product = new Product();
        product.setId(id);
        if (status > 0) {
            //上架
            product.setStatus(1);

        } else {
            //下架
            product.setStatus(-1);
        }
        productMapper.updateById(product);
        return 1;
    }

    /**
     * 查询销量前20的商品的信息
     *
     * @return 返回的是以productSku为主表
     */
    @Override
    public List<ProductSku> getTopSale() {

        return productSkuMapper.getTopSale();
    }

    /**
     * 分页条件查询商品信息
     *
     * @param skuQuery 查询对象
     * @return 返回多个对象
     */
    @Override
    public List<ProductSku> skuList(SkuQuery skuQuery) {
        return productSkuMapper.skuList(skuQuery);
    }


//===================================远程调用======================================================

    /**
     * 查询sku信息
     *
     * @param skuId id
     * @return
     */
/*
    @Override
    public ProductSku getProductSku(Long skuId) {

        try {
            //1.从缓存中获取数据 提高效率
            //先设置一个放在redis中的key 取 放 方便
            String dataKey = "product:sku:" + skuId;
            ProductSku productSku = (ProductSku) redisTemplate.opsForValue().get(dataKey);
            if(productSku !=null){
                //缓存中有数据 缓存击中
                log.info("命中缓存，直接返回，线程ID：{}，线程名称：{}", Thread.currentThread().getId(), Thread.currentThread().getName());
                return productSku;
            }else {
                //2.缓存中没有数据 从数据库中获取数据
                    //3.从数据库获取数据必须先获取分布式锁 防止缓存击穿
                String lockKey = "product:sku:lock:" + skuId;
                    //2.2唯一标识uuid
                String lockVal = UUID.randomUUID().toString().replaceAll("-", "");
                    //2.3获取分布式锁对象
                Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(lockKey, lockVal, 5, TimeUnit.SECONDS);
                if(aBoolean){
                    try {
                        //抢到锁查询业务 放到缓存中
                        log.info("获取锁成功：{}，线程名称：{}", Thread.currentThread().getId(), Thread.currentThread().getName());
                        ProductSku productSkuFromDB = getProductSkuFromDB(skuId);
                        //4.将数据库数据放到缓存中 如果是空数据也会存放 缓解 缓存穿透 (空的1分钟 数据10分钟)
                        int ttl = productSkuFromDB == null ? 60 : 600;
                        redisTemplate.opsForValue().set(dataKey, productSkuFromDB,ttl,TimeUnit.SECONDS);
                        //返回前端
                        return productSkuFromDB;
                    } finally {
                        //释放锁
                        //4.业务执行完毕释放锁
                        String scriptText = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                                "then\n" +
                                "    return redis.call(\"del\",KEYS[1])\n" +
                                "else\n" +
                                "    return 0\n" +
                                "end";
                        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
                        redisScript.setScriptText(scriptText);
                        redisScript.setResultType(Long.class);
                        redisTemplate.execute(redisScript, Arrays.asList(lockKey), lockVal);
                    }
                }else {
                    //没有抢到锁--等待 自旋
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    log.error("获取锁失败，自旋：{}，线程名称：{}", Thread.currentThread().getId(), Thread.currentThread().getName());
                    //自旋
                    return this.getProductSku(skuId);
                }
            }
        } catch (Exception e) {
            //兜底处理方案：Redis服务有问题，将业务数据获取自动从数据库获取
            log.error("[商品服务]查询商品信息异常：{}", e);
            return this.getProductSkuFromDB(skuId);
        }

    }
*/

    //从数据库中获取数据
    private ProductSku getProductSkuFromDB(Long skuId) {
        return productSkuMapper.selectById(skuId);
    }

    /**
     * 查询sku信息
     *
     * @param skuId id
     * @return
     */
    @GuiguCache(prefix = "ProductSku")
    @Override
    public ProductSku getProductSku(Long skuId) {
        return productSkuMapper.selectById(skuId);
    }

    /**
     * 查询product信息
     *
     * @param id 主键id
     * @return
     */
    @GuiguCache(prefix = "product:")
    @Override
    public Product getProduct(Long id) {
        return productMapper.selectById(id);
    }

    /**
     * 查询价格从sku表查询
     *
     * @param skuId 主键id
     * @return
     */
    @GuiguCache(prefix = "skuPrice:")
    @Override
    public SkuPrice getSkuPrice(Long skuId) {
        SkuPrice skuPrice = new SkuPrice();
        ProductSku productSku = productSkuMapper.selectOne(
                new LambdaQueryWrapper<ProductSku>().
                        eq(ProductSku::getId, skuId).
                        select(ProductSku::getSalePrice, ProductSku::getMarketPrice));
        //拷贝返回
        BeanUtils.copyProperties(productSku, skuPrice);
        return skuPrice;

    }

    /**
     * 查询ProductDetails表信息
     *
     * @param id p的主键
     * @return
     */
    @GuiguCache(prefix = "productDetails::")
    @Override
    public ProductDetails getProductDetails(Long id) {

        ProductDetails productDetails = productDetailsMapper.selectOne(
                new LambdaQueryWrapper<ProductDetails>().
                        eq(ProductDetails::getProductId, id));
        return productDetails;
    }

    /**
     * 查新sku_stock表的信息
     *
     * @param skuId 主键skuId
     * @return
     */
    @GuiguCache(prefix = "skuStock:")
    @Override
    public SkuStockVo getSkuStock(Long skuId) {
        SkuStockVo skuStockVo = new SkuStockVo();
        SkuStock skuStock = skuStockMapper.selectOne(
                new LambdaQueryWrapper<SkuStock>().
                        eq(SkuStock::getSkuId, skuId).
                        select(SkuStock::getAvailableNum, SkuStock::getSaleNum)
        );
        BeanUtils.copyProperties(skuStock, skuStockVo);
        return skuStockVo;
    }

    /**
     * 查询sku表的信息 根据p的id 多个 key为sku_spc 值为skuId
     *
     * @param id p的主键id
     * @return
     */
    @GuiguCache(prefix = "skuSpecValue:")
    @Override
    public Map<String, Long> getSkuSpecValue(Long id) {
        List<ProductSku> productSkuList = productSkuMapper.selectList(
                new LambdaQueryWrapper<ProductSku>().
                        eq(ProductSku::getProductId, id));

        //根据p的主键获取多个sku对象
      /* productSkuList.stream().map(item -> {
            Map<String, Long> map = new HashMap<>();
            String skuSpec = item.getSkuSpec();
            Long skuId = item.getId();
            map.put(skuSpec, skuId);
            return map;
        });*/

        Map<String, Long> resultMap = productSkuList.stream()
                .collect(Collectors.toMap(
                        ProductSku::getSkuSpec,  // 键：SKU 规格
                        BaseEntity::getId        // 值：SKU ID
                ));

        return resultMap;

    }

    /**
     * 批量查询商品价格
     * @param skuIdList 商品id
     * @return
     */
    @Override
    public List<SkuPrice> getSkuPriceList(List<Long> skuIdList) {
        if(CollectionUtils.isEmpty(skuIdList)){
            //购物车为空
            return new ArrayList<SkuPrice>();
        }
        List<ProductSku> productSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>()
                .in(ProductSku::getId, skuIdList)
                .select(ProductSku::getId, ProductSku::getSalePrice, ProductSku::getMarketPrice)
        );
        if(CollectionUtils.isEmpty(productSkuList)){
            return new ArrayList<SkuPrice>();
        }
        List<SkuPrice> priceList = productSkuList.stream().map(item -> {
            SkuPrice skuPrice = new SkuPrice();
            skuPrice.setSalePrice(item.getSalePrice());
            skuPrice.setMarketPrice(item.getMarketPrice());
            skuPrice.setSkuId(item.getId());
            return skuPrice;
        }).collect(Collectors.toList());
        return priceList;
    }


    /**
     * 检查库存是否够 并且锁定库存
     * @param orderNo 订单号
     * @param skuLockVoList  订单信息集合表
     * @return
     */
    @Override
    public String checkAndLock(String orderNo, List<SkuLockVo> skuLockVoList) {
        String key = "sku:checkAndLock:" + orderNo;
        String dataKey = "sku:lock:data:" + orderNo; //z防止重复提交
        Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(key, orderNo, 1, TimeUnit.HOURS);
        if(!ifAbsent){
            //没抢到锁
            //判断是否已经修改后 就是已经加锁了
            Boolean aBoolean = redisTemplate.hasKey(dataKey); //无论加锁成功 放入到redis将dadaKey
            if(aBoolean){
                return "";
            }else {
                return "请勿重复提交";
            }
        }

        //检查库存
        skuLockVoList.forEach(skuLockVo -> {
             SkuLockVo skuLockVoDB = skuStockMapper.clear(skuLockVo.getSkuId(),skuLockVo.getSkuNum());
             if(skuLockVoDB==null){
                 skuLockVo.setIsHaveStock(false);
             }else {
                 skuLockVo.setIsHaveStock(true);
             }
        });

        //检查skuLockVoList集合中是否存在至少一个SkuLockVo对象，其getIsHaveStock()方法返回false（即没有库存）。
        // 如果存在这样的对象，整个表达式的结果为true；如果不存在，结果为false。
        if (skuLockVoList.stream().anyMatch(new Predicate<SkuLockVo>() {
            @Override
            public boolean test(SkuLockVo skuLockVo) {
                return !skuLockVo.getIsHaveStock();
            }
        })){
            //只要商品中有一个是库存不足的情况下
            //返回库存不足 加上商品名字 解锁
            //查询库存不够的商品
            List<SkuLockVo> noHaveStockSkuLockVoList = skuLockVoList.stream().filter(item -> {
                return !item.getIsHaveStock();
            }).collect(Collectors.toList());
            StringBuffer sb = new StringBuffer();
            noHaveStockSkuLockVoList.forEach(noHaveStockSkuLockVo ->{
                    sb.append(noHaveStockSkuLockVo.getSkuId() +"库存不够了");
            });
            //解锁
            redisTemplate.delete(key);
            //返回失败的消息
            return sb.toString();
        }else {
            //查询是否存够 锁库存
            skuLockVoList.forEach(skuLockVo -> {
              int  rows = skuStockMapper.lock(skuLockVo.getSkuId(),skuLockVo.getSkuNum());
              if(rows ==0){
               //加锁失败 释放锁
               this.redisTemplate.delete(key);
               throw  new ServiceException("库存锁定失败");
              }
            });
        }

        //正常锁库存后 将dataKey放入到redis中 如果再有线程进来 查看datakey是否存在 存在就直接返回
        redisTemplate.opsForValue().set(dataKey,skuLockVoList);
        return "";
    }

    /**
     * 解锁
     * @param orderNo
     */
    @Override
    public void unlock(String orderNo) {
        String key = "sku:unLock:" + orderNo;
        String dataKey = "sku:lock:data:" + orderNo; //z防止重复提交

        Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(key, orderNo, 1, TimeUnit.HOURS);
        if(!aBoolean){
            //没有抢到锁
            return;
        }

        //从缓存中获取数据
        List<SkuLockVo> skuLockVoList = (List<SkuLockVo>) redisTemplate.opsForValue().get(dataKey);
        if(CollectionUtils.isEmpty(skuLockVoList)){
            //返回 在消费者端会正常消费队列
            return;
        }

        //解锁
        skuLockVoList.forEach(skuLockVo -> {
            int rows =  skuStockMapper.unlock(skuLockVo.getSkuId(),skuLockVo.getSkuNum());
            if(rows ==0){
                //解锁失败
                throw  new ServiceException("解锁失败");
            }
        });

        //删除 datakey 防止无期限占有内存
        redisTemplate.delete(dataKey);
    }

    /**
     * 减库存
     * @param orderNo
     */
    @Override
    public void minus(String orderNo) {
        String key = "sku:minus:" + orderNo;
        String dataKey = "sku:lock:data:" + orderNo; //z防止重复提交

        Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(key, orderNo, 1, TimeUnit.HOURS);
        if(!aBoolean){
            //没有抢到锁
            return;
        }

        //从缓存中获取数据
        List<SkuLockVo> skuLockVoList = (List<SkuLockVo>) redisTemplate.opsForValue().get(dataKey);
        if(CollectionUtils.isEmpty(skuLockVoList)){
            //返回 在消费者端会正常消费队列
            return;
        }

        //减库存
        skuLockVoList.forEach(skuLockVo -> {
            int rows =  skuStockMapper.minus(skuLockVo.getSkuId(),skuLockVo.getSkuNum());
            if(rows ==0){
                //减库存失败 --重试
                throw  new ServiceException("减库存失败");
            }
        });

        //删除 datakey 防止无期限占有内存
        redisTemplate.delete(dataKey);
    }
}